/* -*-c++-*- 
 * This source code is proprietary of ADIT
 * Copyright (C) 2013 Advanced Driver Information Technology Joint Venture GmbH
 * All rights reserved
 *
 * Author: Vadiraj Kaamsha <vadiraj.kaamsha@in.bosch.com>
 * Author: Rudolf Dederer <rudolf.dederer@de.bosch.com>
*/

#ifndef OSGBATCHEDTEXT_TEXTSIMPLE
#define OSGBATCHEDTEXT_TEXTSIMPLE 1

#include <osgBatchedText/TextConfigBase>
#include <osgBatchedText/BatchElementBase>
#include <osgBatchedText/GlyphInfoContainer>

namespace osgBatchedText {

const osg::Vec4 ZERO_VEC4 = osg::Vec4();
const osg::Vec3 ZERO_VEC3 = osg::Vec3();

class OSGBATCHEDTEXT_EXPORT TextSimpleBase : public osg::Referenced
{
public:
    /** constructors */
    TextSimpleBase();
    TextSimpleBase(const TextSimpleBase& src);

    /** setter for configuration class which is shared between similar objects */
    void setTextConfigBase(TextConfigBase* textConfigBase) { _textConfigBase = textConfigBase; }
    /** getter of configuration class which is shared between similar objects */
    const TextConfigBase* getTextConfigBase() const { return _textConfigBase.get(); }

    /** connect to BatcherBase drawable where the text would be drawn */
    void setBatcher(BatcherBase* batcherBase = NULL);
    BatcherBase* getBatcher() const;

    /** set text position relative to _originOffset in _textConfigBase */
    void setPosition(osg::Vec3& pos);
    /** get text position relative to _originOffset in _textConfigBase */
    const osg::Vec3& getPosition() const { return _position; }

    /** set radius of text bounding sphere for culling */
    void setRadius(float radius) { _radius = radius; }
    /** get radius of text bounding sphere for culling */
    float getRadius() const { return _radius; }

    /** set offset of label center to bottom line to make a transition from bottom aligned straight text in 3D to center aligned straight text in 2D */
    void setVerticalOffset3D(float verticalOffset3D) { _verticalOffset3D = verticalOffset3D; }
    /** get offset of label center to bottom line to make a transition from bottom aligned straight text in 3D to center aligned straight text in 2D */
    float getVerticalOffset3D() const { return _verticalOffset3D; }

    void setText(const char* text);
    const char* getText() const { return _text; }

    /** set offset of characters in the character plane as a multiple of character height */
    void setCharOffset(const osg::Vec2& charOffset);

    /** get values from _textConfigBase when valid or default values otherwise */
    const osg::Vec4& getColor() const;
    const osg::Vec4& getBackdropColor() const;
    const osg::Vec3& getOriginOffset() const;
    BatchDrawableBase::tShaderType getShaderType() const;
    float getCharSize() const;
    bool getOutline() const;

    /** get text bounding box in pixels */
    const osg::BoundingBox& getTextBoundingBox() const { return _textBB; }

    /** values stored per line of text */
    struct LineInfo
    {
       LineInfo(float xMin, float xMax, unsigned int numChars) : _xMin(xMin), _xMax(xMax), _numChars(numChars) {}
       /** minimum and maximum horizontal values in pixels from anchor point */
       float _xMin;
       float _xMax;
       /** number of characters in this line */
       unsigned int _numChars;
    };

    struct GlyphInfo
    {
       GlyphInfo() : _glyphIndex(0), _xOffset(0), _yOffset(0), _xAdvance(0), _yAdvance(0) {}

       int _xOffset;
       int _yOffset;
       int _xAdvance;
       int _yAdvance;
       unsigned int _glyphIndex;
    };

    /** calculate glyph positions (with internal call to computeGlyphRepresentationCore) */
    void computeGlyphRepresentation(GlyphInfoContainerBase* glyphInfoContainer, bool useVerticalLayout = false);
    /** calculate glyph positions providing additional informations about line extends and texture positions */
    bool computeGlyphRepresentationCore(GlyphInfoContainerBase* glyphInfoContainer, std::vector<osg::Vec2>& positionOffset,
                                        std::vector<LineInfo>& lineInfo, std::vector<osg::Vec2>& vertices, bool useVerticalLayout = false);

    /** get indication if glyph representation is calculated */
    bool isGlyphRepresentationCalculated() const { return _glyphRepresentationCalculated; }

    /** set indication if glyph representation is calculated */
    void setGlyphRepresentationCalculated() { _glyphRepresentationCalculated = true; }

    /** all glyphs to be drawn in this text object will be fetched from FreeType and added to glyph container of corresponding BatcherBase */
    void addGlyphsToBatch();

    /** TextSimpleBase object has to be added to BatcherBase to be drawn */
    void addTextToBatch(float alpha = 1.0f, bool enableDepthTest = true, const osg::View* view = NULL, const FloatAndHalfFloat& vertexCompW = FloatAndHalfFloat(1.0f)) const;
    void addTextToBatch(const osg::ref_ptr<BatchElementContainer>& batchElementContainer, const osg::View* view = NULL) const;

    void rotateChars(const std::vector<osg::Vec2>& vertices2D, const std::vector<osg::Vec3>& directions, const std::vector<osg::Vec2>& positionOffset, const osg::Vec3& normUpVec, float pitchAng);

    /** set relative character position for curved text with respect to text position */
    void setRelCharPositions(const std::vector<osg::Vec3>& relCharPositions);
    /** get relative character position with respect to text position of "index" character */
    osg::Vec3 getRelCharPosition(unsigned int index) const;
    /** get relative character position factor for curved text with respect to text position */
    float getRelCharPositionScaleFactor() const { return _relCharPosScaleFactor; }

    /** set vector of vertices converting them to half float */
    void setVertices(const std::vector<osg::Vec3>& vertices);
    void setVertices(const std::vector<osg::Vec2>& vertices);

    /** calculate vertex with index "index" at pitch provided by "cosPitchAngleFactor" for shade, "w" is directly set by provided value to be used for different features */
    /** as float */
    osg::Vec4 getVertexFloat(unsigned int index, float w, float cosPitchAngleFactor) const;
    /** as half float for systems with half float support */
    osg::Vec4h getVertexHalfFloat(unsigned int index, half_float::half w, float cosPitchAngleFactor) const;

    const std::vector<osg::Vec3h>& getVertices() const { return _vertices; }

    /** character codes or glyph indices for the text; Returns true if the vector is filled with char codes; returns false if the vector is filled with glyph indices */
    const std::vector<unsigned int>& getCharCodesOrGlyphIndices(bool& areCharCodes) const;

    /** Returns true if the instance contains char-codes for text; returns false if it contains glyph-indices*/
    bool hasCharCodes() const { return _hasCharCodes; }

    /** setter for display priority */
    void setDisplayOrder(unsigned char value) { _displayOrder = value; }
    /** getter for display priority */
    unsigned char getDisplayOrder() const { return _displayOrder; }

    unsigned int getNumDrawableChars() const { return _numDrawableChars; }

protected:
    /** destructor */
    virtual ~TextSimpleBase();

    struct ComputeVerticesInput
    {
       ComputeVerticesInput() : _charCodeOrGlyphCount(0), _lineInfoSize(0), _layout(osgText::TextBase::LEFT_TO_RIGHT), _utf8Strings(NULL), _glyphInfoVector(NULL) {}

       unsigned int _charCodeOrGlyphCount;
       unsigned int _lineInfoSize;
       osgText::TextBase::Layout _layout;
       std::vector<std::string> *_utf8Strings;
       std::vector< std::vector<GlyphInfo> > *_glyphInfoVector;
    };

    /** calculate left offset with respect to configured alignment */
    float calcLeftOffset(LineInfo& lineInfo);
    /** calculate bottom offset with respect to configured alignment */
    float calcBottomOffset(unsigned int lineCount);
    /** Split multi-line string into multiple strings */
    unsigned int splitIntoStringPerLine(const char* inputText, std::vector<std::string>& outputStrings) const;
    /** Compute vertices and position offsets for glyphs using either the utf8Strings or the glyphInfoVector */
    bool computeGlyphVertices(const ComputeVerticesInput& input, GlyphInfoContainerBase* glyphInfoContainer, std::vector<osg::Vec2>& positionOffset, std::vector<LineInfo>& lineInfo,
       std::vector<osg::Vec2>& vertices);

    /** text string to be displayed*/
    char* _text;

    /** offset of characters in the character plane as a multiple of character height */
    osg::Vec2 _charOffset;
    /** text position */
    osg::Vec3 _position;
    /** radius of text bounding sphere for culling */
    float _radius;
    /** offset of label center to bottom line to make a transition from bottom aligned straight text in 3D to center aligned straight text in 2D */
    float _verticalOffset3D;

    /** relative character position for curved text with respect to text position */
    float _relCharPosScaleFactor;
    std::vector<osg::Vec3h> _relCharPosition;

    /** texture vertices's coordinates with respect to text position (straight text) or relative character position (curved text) */
    std::vector<osg::Vec3h> _vertices;

    /** character codes or glyph indices for the text; The "_hasGlyphIndices" flag indicates the nature of data in this container*/
    std::vector<unsigned int> _charCodesOrGlyphIndices;

    /** configuration shared between similar objects */
    osg::ref_ptr<TextConfigBase> _textConfigBase;

    /** text bounding box in pixels */
    osg::BoundingBox _textBB;

    /** indicates that the position is set and valid */
    bool _has_position;

    /** indicates that glyph representation is calculated */
    bool _glyphRepresentationCalculated;

    /** display priority for drawing used to sort simple text when drawing order is switched to priority based */
    unsigned char _displayOrder;

    unsigned int _numDrawableChars;

    /** Flag indicating whether  the _charCodesOrGlyphIndices container has glyph indices(false) or char codes(true)*/
    bool _hasCharCodes;

private:
    TextSimpleBase& operator=(const TextSimpleBase&) /* = delete */;
};

inline const std::vector<unsigned int>& TextSimpleBase::getCharCodesOrGlyphIndices(bool& areCharCodes) const
{
   areCharCodes = _hasCharCodes;
   return _charCodesOrGlyphIndices;
}

inline const osg::Vec4& TextSimpleBase::getColor() const
{
   return _textConfigBase.valid() ? _textConfigBase->getColor() : ZERO_VEC4;
}

inline const osg::Vec4& TextSimpleBase::getBackdropColor() const
{
   return _textConfigBase.valid() ? _textConfigBase->getBackdropColor() : ZERO_VEC4;
}

inline const osg::Vec3& TextSimpleBase::getOriginOffset() const
{
   return _textConfigBase.valid() ? _textConfigBase->getOriginOffset() : ZERO_VEC3;
}

inline BatchDrawableBase::tShaderType TextSimpleBase::getShaderType() const
{
   return _textConfigBase.valid() ? _textConfigBase->getShaderType() : BatchDrawableBase::STRAIGHT_SCREEN;
}

inline float TextSimpleBase::getCharSize() const
{
   return _textConfigBase.valid() ? _textConfigBase->getCharSize() : 0.0f;
}

inline bool TextSimpleBase::getOutline() const
{
   return _textConfigBase.valid() ? _textConfigBase->hasOutline() : false;
}

inline void TextSimpleBase::setText(const char* text)
{
   unsigned int lenText = (text != NULL ? strlen(text) + 1u : 1u);
   if (_text)
   {
      delete[] _text;
      _text = NULL;
   }
   if (lenText > 1u)
   {
      _text = new char[lenText];
      if (_text)
      {
         memcpy(_text, text, lenText);
      }
   }
}

inline void TextSimpleBase::setPosition(osg::Vec3& pos)
{
   _position = pos;
   _has_position = true;
}

inline void TextSimpleBase::setVertices(const std::vector<osg::Vec3>& vertices)
{
   convertToHalfFloatVector(vertices, _vertices);
}

inline void TextSimpleBase::setVertices(const std::vector<osg::Vec2>& vertices)
{
   convertToHalfFloatVector(vertices, _vertices);
}

inline osg::Vec4 TextSimpleBase::getVertexFloat(unsigned int index, float w, float cosPitchAngleFactor) const
{
   return osg::Vec4f(_vertices[index].x(), _vertices[index].y() + _verticalOffset3D * cosPitchAngleFactor, _vertices[index].z(), w);
}

inline osg::Vec4h TextSimpleBase::getVertexHalfFloat(unsigned int index, half_float::half w, float cosPitchAngleFactor) const
{
   return osg::Vec4h(_vertices[index].x(), fabsf(cosPitchAngleFactor) > 0.001f ? static_cast<half_float::half>(_vertices[index].y() + _verticalOffset3D * cosPitchAngleFactor) : _vertices[index].y(),
                     _vertices[index].z(), w);
}

inline void TextSimpleBase::setRelCharPositions(const std::vector<osg::Vec3>& relCharPositions)
{
   _relCharPosScaleFactor = convertToScaledHalfFloatVector(relCharPositions, _relCharPosition);
}

inline osg::Vec3 TextSimpleBase::getRelCharPosition(unsigned int index) const
{
   return (!_relCharPosition.size()) ? osg::Vec3() : osg::Vec3(_relCharPosition[index].x() * _relCharPosScaleFactor,
                                                               _relCharPosition[index].y() * _relCharPosScaleFactor,
                                                               _relCharPosition[index].z() * _relCharPosScaleFactor);
}


}

#endif
